package org.jeecg.modules.workflow.controller;

import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.api.R;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.extern.slf4j.Slf4j;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.Model;
import org.activiti.engine.repository.ProcessDefinition;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.modules.workflow.dto.FlowData;
import org.jeecg.modules.workflow.dto.ProcessQuery;
import org.jeecg.modules.workflow.entity.WfForm;
import org.jeecg.modules.workflow.mapper.ActivitiMapper;
import org.jeecg.modules.workflow.service.IProcessEngine;
import org.jeecg.modules.workflow.service.WorkFlowService;
import org.jeecg.modules.workflow.service.ProcessFormService;
import org.jeecg.modules.workflow.service.WfFormService;
import org.jeecg.modules.workflow.utils.ProcessKit;
import org.jeecg.modules.workflow.vo.ModelVO;
import org.jeecg.modules.workflow.vo.ProcessQueryVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;

/**
 * 工作流程控制层
 * Create by Kalvin on 2020/4/20.
 */
@Slf4j
@RestController
@RequestMapping(value = "workflow")
public class WorkflowController {
    @Resource
    private RepositoryService repositoryService;

    @Autowired
    private IProcessEngine processEngine;

    @Resource
    private WfFormService formService;

    @Resource
    private ProcessFormService processFormService;

    @Resource
    private WorkFlowService workFlowService;

    @Resource
    private ActivitiMapper activityMapper;

    @PostMapping(value = "/deploymentStrXmlBpmn")
    public void deploymentStrXmlBpmn(String modelId, String xml) {
        String modelName = "", processName = "";
        try {
            // 获取模型
            Model modelData = repositoryService.getModel(modelId);
            modelName = modelData.getName();
            if(modelName == "")
                modelName = "test";

//            byte[] bytes = repositoryService.getModelEditorSource(modelData.getId());
//            if (bytes == null) {
//                throw new RuntimeException("模型数据为空，请先设计流程并成功保存，再进行发布。");
//            }
//            JsonNode modelNode = new ObjectMapper().readTree(bytes);
//            BpmnModel model = new BpmnJsonConverter().convertToBpmnModel(modelNode);
//            if (model.getProcesses().size() == 0) {
//                throw new RuntimeException("数据模型不符要求，请至少设计一条主线流程。");
//            }
//            byte[] bpmnBytes = new BpmnXMLConverter().convertToXML(model);

            //发布流程
            String processSourceName = modelName + ".bpmn20.xml";
            Deployment deployment = repositoryService.createDeployment()
                    .name(modelName)
                    .addString(processSourceName, xml)
                    .deploy();

            // 发布版本+1
            Integer version = modelData.getVersion();
            modelData.setVersion(StrUtil.isBlank(modelData.getDeploymentId()) ? version : version + 1);
            modelData.setDeploymentId(deployment.getId());
            repositoryService.saveModel(modelData);

            ProcessDefinition processDefinition = ProcessKit.getProcessDefinition(deployment.getId());
            processName = processDefinition.getName();
            if (StrUtil.isBlank(processName)) {
                processName = modelName;
                activityMapper.updateProcessDefinitionName(processName, processDefinition.getId());
            }

        } catch (Exception e) {
            log.error("流程【{}】发布失败：{}", processName, e.getMessage());
            throw new RuntimeException("流程发布失败：" + e.getMessage());
        }
    }

    // 导出xml格式的bpmn文件
    @GetMapping(value = "exportBpmn")
    public ModelAndView exportBpmn(@RequestParam String modelId, HttpServletResponse response) throws Exception {
        BufferedOutputStream bos = null;

        Model model = repositoryService.getModel(modelId);
        ObjectNode modelNode = (ObjectNode) new ObjectMapper().readTree(repositoryService.getModelEditorSource(model.getId()));
        byte[] bpmnBytes = new BpmnXMLConverter().convertToXML(new BpmnJsonConverter().convertToBpmnModel(modelNode));

//        String str = new String(bpmnBytes, "utf-8");
//        System.out.println(str);

        // 封装输出流
        bos = new BufferedOutputStream(response.getOutputStream());
        bos.write(bpmnBytes);// 写入流

        String filename = model.getName() + ".bpmn20.xml";
        response.setContentType("application/x-msdownload;");
        response.setHeader("Content-Disposition",
                "attachment; filename=" + filename);
        response.flushBuffer();

        return null;
    }


    /**
     * 导出流程设计文件（.json）
     * @param modelIds 模型ID集合
     */
    @GetMapping(value = "export/batch")
    public void exportBatch(@RequestParam("modelIds") List<String> modelIds) {
        ProcessQuery<JSONObject> processesJson = workFlowService.getProcessesJson(modelIds);
        JSONObject data = processesJson.getData();
        String name = data.get("name").toString();
        String version = data.get("version").toString();
        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        try {
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(name, "UTF-8") + "_v" + version + ".json");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        ServletUtil.write(response, data.toString(), "application/json;charset=UTF-8");
    }

    /**
     * 导入流程设计
     * @param file 流程设计文件（.json）
     * @return r
     */
    @PostMapping(value = "import/batch")
    public Result importBatch(@RequestParam(value = "file") MultipartFile file) {
        try {
            workFlowService.importProcesses(file.getInputStream());
        } catch (IOException e) {
            return Result.error(e.getMessage());
        }
        return Result.ok();
    }

    /**
     * 保存模型
     * @param modelVO 模型VO实体
     * @return r
     */
    @PostMapping(value = "save/model")
    public Result saveModel(@RequestBody ModelVO modelVO) {
        final String modelId = workFlowService.create(modelVO);
        return Result.ok("添加成功");
    }

    /**
     * 保存表单
     * @param form 表单VO实体
     * @return r
     */
    @PostMapping(value = "save/form")
    public Result saveForm(@RequestBody WfForm form) {
        if(null == form.getId()) {
            formService.saveForm(form);
        } else{
            formService.updateById(form);
        }
        return Result.ok("操作成功");
    }

    /**
     * 查询表单
     * @return r
     */
    @GetMapping(value = "getform")
    public Result saveForm(@RequestParam String modelId) {
        WfForm wfForm = formService.getOne(new LambdaQueryWrapper<WfForm>().eq(WfForm::getModelId,modelId));
        return Result.ok(wfForm);
    }

    /**
     * 流程管理列表
     * @param processQueryVO 查询参数
     * @return r
     */
    @GetMapping(value = "process/list")
    public Result processList(ProcessQueryVO processQueryVO) {
        return Result.ok(workFlowService.getProcesses(processQueryVO));
    }

    /**
     * 发布/部署流程
     * @param modelId 模型ID
     * @return r
     */
    @PostMapping(value = "deploy/{modelId}")
    public Result deploy(@PathVariable String modelId) {
        return Result.ok(workFlowService.deploy(modelId));
    }

    /**
     * 当前用户启动流程
     * @param deploymentId 流程发布ID
     */
    @GetMapping(value = "process/{deploymentId}/start")
    public Result start(@PathVariable String deploymentId) {
        final String taskId = processEngine.start(deploymentId);
        return Result.ok(taskId);
    }

    /**
     * 删除模型
     * @param modelId 模型ID
     * @return r
     */
    @DeleteMapping(value = "delete")
    public Result delete(@RequestParam(name="id",required=true) String modelId) {
        workFlowService.delete(modelId);
        return Result.ok();
    }

    /**
     * 挂起流程
     * @param deploymentIds 发布ID集合
     * @return r
     */
    @PostMapping(value = "suspend/process")
    public Result suspendProcess(@RequestParam("deploymentIds") List<String> deploymentIds) {
        processEngine.suspendProcessDefinitionByIds(deploymentIds);
        return Result.ok();
    }

    /**
     * 激活流程
     * @param deploymentIds 发布ID集合
     * @return r
     */
    @PostMapping(value = "activate/process")
    public Result activateProcess(@RequestParam("deploymentIds") List<String> deploymentIds) {
        processEngine.activateProcessDefinitionByIds(deploymentIds);
        return Result.ok();
    }

    /**
     * 挂起流程实例
     * @param processInstanceId 流程实例ID
     * @return r
     */
    @PostMapping(value = "suspend/processInstance/{processInstanceId}")
    public Result suspendProcessInstance(@PathVariable String processInstanceId) {
        processEngine.suspendProcessInstance(processInstanceId);
        return Result.ok();
    }

    /**
     * 激活流程实例
     * @param processInstanceId 流程实例ID
     * @return r
     */
    @PostMapping(value = "activate/processInstance/{processInstanceId}")
    public Result activateProcessInstance(@PathVariable String processInstanceId) {
        processEngine.activateProcessInstance(processInstanceId);
        return Result.ok();
    }

    /**
     * 我的流程列表
     * @param processQueryVO 查询参数
     * @return r
     */
    @GetMapping(value = "myprocess/list")
    public Result myProcessList(ProcessQueryVO processQueryVO) {
        processQueryVO.setUsername(((LoginUser) SecurityUtils.getSubject().getPrincipal()).getId());
        return Result.ok(workFlowService.getDeployProcesses(processQueryVO));
    }

    @GetMapping(value = "processInstance/{processInstanceId}")
    public Result processInstance(@PathVariable String processInstanceId) {
        return Result.ok(workFlowService.getHistoricProcessInstanceByProcessInstanceId(processInstanceId));
    }

    /**
     * 流程实例流转意见
     * @param processInstanceId 实例ID
     * @return r
     */
    @GetMapping(value = "processInstance/{processInstanceId}/comments")
    public Result processInstanceComments(@PathVariable String processInstanceId) {
        return Result.ok(workFlowService.getProcessInstanceComments(processInstanceId).getData());
    }

    /**
     * 我的待办列表
     * @param processQueryVO 查询参数
     * @return r
     */
    @GetMapping(value = "mytodo/list")
    public Result myTodoList(ProcessQueryVO processQueryVO) {
        processQueryVO.setUsername(((LoginUser) SecurityUtils.getSubject().getPrincipal()).getUsername());
        return workFlowService.getTodoTasks(processQueryVO).toR();
    }

    /**
     * 我的已办列表
     * @param processQueryVO 查询参数
     * @return r
     */
    @GetMapping(value = "mydone/list")
    public Result myDoneList(ProcessQueryVO processQueryVO) {
        processQueryVO.setUsername(((LoginUser) SecurityUtils.getSubject().getPrincipal()).getUsername());
        return workFlowService.getDoneTasks(processQueryVO).toR();
    }

    /**
     * 我的申请列表
     * @param processQueryVO 查询参数
     * @return r
     */
    @GetMapping(value = "myapply/list")
    public Result myApplyList(ProcessQueryVO processQueryVO) {
        processQueryVO.setUsername(((LoginUser) SecurityUtils.getSubject().getPrincipal()).getUsername());
        return workFlowService.getApplyTasks(processQueryVO).toR();
    }

    /**
     * 撤回任务
     * @param taskId 已办任务ID
     * @return r
     */
    @PostMapping(value = "withdraw/task/{taskId}")
    public Result withdrawTask(@PathVariable String taskId) throws Exception{
        String userName = ((LoginUser) SecurityUtils.getSubject().getPrincipal()).getUsername();
        workFlowService.withdrawApproval(userName,taskId);
        return Result.ok();
    }

    @PostMapping(value = "revoke/{processInstanceId}")
    public Result revoke(@PathVariable String processInstanceId) throws Exception{
        String userName = ((LoginUser) SecurityUtils.getSubject().getPrincipal()).getUsername();
        workFlowService.revoke(processInstanceId,userName);
        return Result.ok();
    }

    @PostMapping("backToFirstNode")
    public Result backToFirstNode(@RequestBody FlowData flowData){

        workFlowService.backToFirstNode(flowData.getTaskId());
        return Result.ok();
    }
}
